import datetime
import tasks
from enum import Enum
from typing import Union
from tasks import Task
from utils.utils import *
from utils.parameters import *


class Exp_apporach(Enum):
    central = 0     #0000
    fl = 1          #0001
    clip = 2        #0010
    noise = 4       #0100
    assist = 8      #1000
    

class Helper:
    paths = {}
    start_week:str
    start_day:str
    start_time:str
    save_path:str
    save_name:str
    params:Union[Params]
    task:Union[Task]
    exp_approach:Exp_apporach = Exp_apporach.central.value
    si:bool#show info


    def __init__(self,args):
        self.si= args.show_info
        self.start_week,self.start_day,self.start_time,self.save_name = self._init_time()
        self._init_path(args)
        self._init_params(args)
        self._init_save_path()
        self.task = self._init_task()
        

    def _init_save_path(self) -> str:
        #get exp_name
        _exp_name:str = ''
        _save_path:str = ''
        #fl or central
        _exp_approach:int = self.exp_approach
        if _exp_approach % 2 == 1:
            _exp_name += 'fed'
            _save_path = self.paths['fl']
        else:
            _exp_name += 'cen'
            _save_path = self.paths['central']
        _exp_approach = _exp_approach >> 1
        #get other append methods
        _exp_len:int = len(Exp_apporach) - 2
        for i in range(_exp_len):
            if _exp_approach % 2 == 1:
                _exp_name += '_'
                v2find = 2 ** (i + 1)
                for key,v in Exp_apporach.__members__.items():
                    if v.value == v2find:
                        _exp_name += key#append method name
                        print(key)
                        break
            _exp_approach = _exp_approach >> 1
        #get save path
        self.save_path = os.path.join(_save_path,_exp_name)
        if not os.path.exists(self.save_path):
            os.mkdir(self.save_path)


    def _init_path(self,args) -> None:
        """
        description:
            init dir,if dir is not exist, create new dir
        params:
            -
        ret:
            @dataset_path:
            @save_paths:
        """
        #get base_config
        base_config = read_config(args.base_config)
        #mkdir
        path_mkdir(base_config)
        #dataset path
        self.paths['data_path'] = base_config['data_path']
        #result path
        self.paths['results_path'] = base_config['results_path']
        self.paths['fl'] = base_config['fl_ret_path']
        self.paths['central'] = base_config['cen_ret_path']


    def _init_time(self):
        """
        description:
            get cur time
        params:
            -
        ret:
            @start_week:
            @start_day:
            @start_time:
        """
        start_week = datetime.datetime.now().strftime("%A")
        start_day = datetime.datetime.now().strftime("%Y-%m-%d")
        start_time = datetime.datetime.now().strftime("%I:%M%p")
        save_name = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        if self.si:
            print(f"execute start time: {start_week}|{start_day}|{start_time}")
        return start_week,start_day,start_time,save_name


    def _init_params(self,args) -> Params:
        """
        description:
            init experiment params
        params:
            @args:experiment's paramters
        ret:
            @params:params
        """
        #priority params
        self.params = Params(args,self.paths)

        #show info
        if args.show_info:
            print(f"Using {self.params.device} device")
        

    def _init_task(self) -> Task:
        """
        description:
            init experiment task by dst
        params:
            @params:
        ret:
            @_task:
        """
        _task:Union[Task] = None
        dst:str = self.params.dst
        if type(self.params) == Params:
            if dst == "mnist":
                _task = tasks.MnistTask(self.params)
            elif dst == "facescrub":
                _task = tasks.FacescrubTask(self.params)
            else:
                pass

        return _task